package cn.iocoder.yudao.module.system.task;

import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.dal.dataobject.examtest.ExamTestDO;
import cn.iocoder.yudao.module.system.dal.mysql.examtest.ExamTestMapper;
import cn.iocoder.yudao.module.system.dal.mysql.examscore.ExamScoreMapperOptimized;
import cn.iocoder.yudao.module.system.service.teacherdashboard.AsyncTeacherDashboardService;
import cn.iocoder.yudao.module.system.service.teacherdashboard.TeacherDashboardCacheService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 教师看板数据预计算定时任务
 * 定期预计算和缓存热点数据，提升查询性能
 *
 * @author 芋道源码
 */
@Component
@Slf4j
@ConditionalOnProperty(name = "teacher.dashboard.precompute.enabled", havingValue = "true", matchIfMissing = true)
public class TeacherDashboardPrecomputeTask {

    @Resource
    private ExamTestMapper examTestMapper;

    @Resource
    private ExamScoreMapperOptimized examScoreMapperOptimized;

    @Resource
    private TeacherDashboardCacheService cacheService;

    @Resource
    private AsyncTeacherDashboardService asyncService;

    // ==================== 定时任务配置 ====================

    /**
     * 每5分钟预计算最近考试的统计数据
     */
    @Scheduled(fixedRate = 300000) // 5分钟
    public void precomputeRecentExamStatistics() {
        log.debug("开始预计算最近考试统计数据");
        
        try {
            // 获取最近7天的考试
            List<ExamTestDO> recentExams = getRecentExams(7);
            
            if (recentExams.isEmpty()) {
                log.debug("没有找到最近的考试数据");
                return;
            }
            
            log.info("找到{}个最近考试，开始预计算统计数据", recentExams.size());
            
            for (ExamTestDO exam : recentExams) {
                precomputeExamStatistics(exam.getId());
            }
            
            log.info("最近考试统计数据预计算完成，处理考试数量: {}", recentExams.size());
            
        } catch (Exception e) {
            log.error("预计算最近考试统计数据失败", e);
        }
    }

    /**
     * 每15分钟预计算班级排名数据
     */
    @Scheduled(fixedRate = 900000) // 15分钟
    public void precomputeClassRankings() {
        log.debug("开始预计算班级排名数据");
        
        try {
            // 获取最近3天的考试
            List<ExamTestDO> recentExams = getRecentExams(3);
            
            if (recentExams.isEmpty()) {
                log.debug("没有找到最近的考试数据");
                return;
            }
            
            int totalPrecomputed = 0;
            
            for (ExamTestDO exam : recentExams) {
                List<Long> classIds = extractClassIds(exam);
                
                for (Long classId : classIds) {
                    precomputeClassRanking(classId, exam.getId());
                    totalPrecomputed++;
                }
            }
            
            log.info("班级排名数据预计算完成，处理班级数量: {}", totalPrecomputed);
            
        } catch (Exception e) {
            log.error("预计算班级排名数据失败", e);
        }
    }

    /**
     * 每30分钟预计算科目统计数据
     */
    @Scheduled(fixedRate = 1800000) // 30分钟
    public void precomputeSubjectStatistics() {
        log.debug("开始预计算科目统计数据");
        
        try {
            // 获取最近5天的考试
            List<ExamTestDO> recentExams = getRecentExams(5);
            
            if (recentExams.isEmpty()) {
                log.debug("没有找到最近的考试数据");
                return;
            }
            
            for (ExamTestDO exam : recentExams) {
                precomputeSubjectStatistics(exam.getId());
            }
            
            log.info("科目统计数据预计算完成，处理考试数量: {}", recentExams.size());
            
        } catch (Exception e) {
            log.error("预计算科目统计数据失败", e);
        }
    }

    /**
     * 每天凌晨2点执行全量数据预计算
     */
    @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点
    public void fullDataPrecompute() {
        log.info("开始执行全量数据预计算");
        
        try {
            // 清理过期缓存
            cleanExpiredCache();
            
            // 获取最近30天的考试
            List<ExamTestDO> allExams = getRecentExams(30);
            
            if (allExams.isEmpty()) {
                log.info("没有找到考试数据，跳过全量预计算");
                return;
            }
            
            log.info("开始全量预计算，考试数量: {}", allExams.size());
            
            // 异步执行全量预计算
            CompletableFuture<Void> future = asyncService.batchPreloadCacheAsync(
                allExams.stream().map(ExamTestDO::getId).collect(Collectors.toList()),
                extractAllClassIds(allExams)
            );
            
            // 等待完成（最多等待30分钟）
            future.get(30, java.util.concurrent.TimeUnit.MINUTES);
            
            log.info("全量数据预计算完成");
            
        } catch (Exception e) {
            log.error("全量数据预计算失败", e);
        }
    }

    /**
     * 每小时清理过期缓存
     */
    @Scheduled(fixedRate = 3600000) // 1小时
    public void cleanExpiredCache() {
        log.debug("开始清理过期缓存");
        
        try {
            // 获取缓存统计信息
            Map<String, Object> cacheStats = cacheService.getCacheStatistics();
            log.debug("当前缓存统计: {}", cacheStats);
            
            // 这里可以添加具体的过期缓存清理逻辑
            // 例如：清理超过一定时间的缓存项
            
            log.debug("过期缓存清理完成");
            
        } catch (Exception e) {
            log.error("清理过期缓存失败", e);
        }
    }

    /**
     * 每天凌晨3点更新排名数据
     */
    @Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点
    public void updateRankings() {
        log.info("开始更新排名数据");
        
        try {
            // 获取最近7天的考试
            List<ExamTestDO> recentExams = getRecentExams(7);
            
            for (ExamTestDO exam : recentExams) {
                // 异步更新排名
                asyncService.updateRankingsAsync(exam.getId());
            }
            
            log.info("排名数据更新任务已提交，考试数量: {}", recentExams.size());
            
        } catch (Exception e) {
            log.error("更新排名数据失败", e);
        }
    }

    // ==================== 私有方法 ====================

    /**
     * 预计算单个考试的统计数据
     */
    private void precomputeExamStatistics(Long examId) {
        try {
            // 检查缓存是否已存在
            if (cacheService.getExamStatistics(examId) != null) {
                log.debug("考试统计缓存已存在，跳过预计算，examId: {}", examId);
                return;
            }
            
            // 查询统计数据
            Map<String, Object> stats = examScoreMapperOptimized.selectBatchStatistics(examId);
            
            if (stats != null && !stats.isEmpty()) {
                // 缓存统计数据
                cacheService.cacheExamStatistics(examId, stats);
                log.debug("考试统计数据预计算完成，examId: {}", examId);
            }
            
        } catch (Exception e) {
            log.warn("预计算考试统计数据失败，examId: {}, error: {}", examId, e.getMessage());
        }
    }

    /**
     * 预计算班级排名数据
     */
    private void precomputeClassRanking(Long classId, Long examId) {
        try {
            // 检查缓存是否已存在
            if (cacheService.getClassRanking(classId, examId) != null) {
                log.debug("班级排名缓存已存在，跳过预计算，classId: {}, examId: {}", classId, examId);
                return;
            }
            
            // 查询前50名学生
            List<Map<String, Object>> ranking = examScoreMapperOptimized.selectOptimizedClassTopStudents(
                classId, examId, 50);
            
            if (ranking != null && !ranking.isEmpty()) {
                // 缓存排名数据
                cacheService.cacheClassRanking(classId, examId, ranking);
                log.debug("班级排名数据预计算完成，classId: {}, examId: {}, size: {}", 
                         classId, examId, ranking.size());
            }
            
        } catch (Exception e) {
            log.warn("预计算班级排名数据失败，classId: {}, examId: {}, error: {}", 
                    classId, examId, e.getMessage());
        }
    }

    /**
     * 预计算科目统计数据
     */
    private void precomputeSubjectStatistics(Long examId) {
        try {
            // 检查缓存是否已存在
            if (cacheService.getSubjectStatistics(examId) != null) {
                log.debug("科目统计缓存已存在，跳过预计算，examId: {}", examId);
                return;
            }
            
            // 查询科目统计数据
            List<Map<String, Object>> stats = examScoreMapperOptimized.selectBatchSubjectStatistics(examId);
            
            if (stats != null && !stats.isEmpty()) {
                // 缓存科目统计数据
                cacheService.cacheSubjectStatistics(examId, stats);
                log.debug("科目统计数据预计算完成，examId: {}, count: {}", examId, stats.size());
            }
            
        } catch (Exception e) {
            log.warn("预计算科目统计数据失败，examId: {}, error: {}", examId, e.getMessage());
        }
    }

    /**
     * 获取最近N天的考试
     */
    private List<ExamTestDO> getRecentExams(int days) {
        try {
            LocalDateTime startTime = LocalDateTime.now().minusDays(days);
            
            // 这里需要根据实际的ExamTestMapper方法来查询
            // 示例查询逻辑
            return examTestMapper.selectList(new LambdaQueryWrapperX<ExamTestDO>()
                    .eq(ExamTestDO::getExamDate, startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
            
        } catch (Exception e) {
            log.warn("获取最近考试失败，days: {}, error: {}", days, e.getMessage());
            return List.of();
        }
    }

    /**
     * 从考试中提取班级ID列表
     */
    private List<Long> extractClassIds(ExamTestDO exam) {
        try {
            // 直接获取班级ID列表
            List<Long> classIds = exam.getClassIds();
            
            if (classIds != null && !classIds.isEmpty()) {
                return classIds;
            }
            
            return List.of();
            
        } catch (Exception e) {
            log.warn("提取班级ID失败，examId: {}, error: {}", exam.getId(), e.getMessage());
            return List.of();
        }
    }

    /**
     * 从所有考试中提取所有班级ID
     */
    private List<Long> extractAllClassIds(List<ExamTestDO> exams) {
        return exams.stream()
                .flatMap(exam -> extractClassIds(exam).stream())
                .distinct()
                .collect(Collectors.toList());
    }

    // ==================== 任务监控方法 ====================

    /**
     * 获取预计算任务统计信息
     */
    public Map<String, Object> getPrecomputeStatistics() {
        try {
            Map<String, Object> cacheStats = cacheService.getCacheStatistics();
            
            return Map.of(
                "lastUpdateTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                "cacheStatistics", cacheStats,
                "taskStatus", "running"
            );
            
        } catch (Exception e) {
            log.error("获取预计算统计信息失败", e);
            return Map.of(
                "error", e.getMessage(),
                "taskStatus", "error"
            );
        }
    }
}